package javax.realtime;

public class RelativeTime extends HighResolutionTime {

	public RelativeTime()
	{
		this(0,0,null);
	}
	
	public RelativeTime(Clock clock)
	{
		this(0,0,clock);
	}
	
	public RelativeTime(long millis, int nanos)
	{
		this(millis, nanos, null);
	}
	
	public RelativeTime(long millis, int nanos, Clock clock)
	{
		if(!_normalise(millis, nanos)) throw new IllegalArgumentException();
		this.milliseconds = millis;
		this.nanoseconds = nanos;
		if(clock == null) clock = Clock.getRealtimeClock();
		this.clock = clock;
	}
	
	public RelativeTime(RelativeTime time)
	{
		if(time == null) throw new IllegalArgumentException();
		milliseconds = time.milliseconds;
		nanoseconds = time.nanoseconds;
		clock = time.clock;
	}
	
	public RelativeTime(RelativeTime time, Clock clock)
	{
		if(time == null) throw new IllegalArgumentException();
		if(clock == null) clock = Clock.getRealtimeClock();
		milliseconds = time.milliseconds;
		nanoseconds = time.nanoseconds;
		this.clock = clock;
	}
	
	public AbsoluteTime absolute(Clock clock)
	{
		return absolute(clock, null);
	}
	
	public AbsoluteTime absolute(Clock clock, AbsoluteTime dest)
	{
		if(clock == null) clock = Clock.getRealtimeClock();
		if(dest == null) dest = new AbsoluteTime(0, 0, clock);
		//dest.clock = clock;
		
		return clock.getTime(dest).add(this.milliseconds, this.nanoseconds, dest);
	}
	
	public RelativeTime add(long millis, int nanos)
	{
		return add(millis, nanos, null);
	}
	
	public RelativeTime add(long millis, int nanos, RelativeTime dest)
	{
		if(dest == null) dest = new RelativeTime(0,0,this.clock);
		if (!dest._setnormalised(addSafe(this.milliseconds, millis), this.nanoseconds + nanos)) throw new ArithmeticException();
		dest.clock = clock;
		return dest;
	}
	
	public RelativeTime add(RelativeTime time)
	{
		return add(time, null);
	}
	
	public RelativeTime add(RelativeTime time, RelativeTime dest)
	{
		if(time == null) throw new IllegalArgumentException();
		if(time.clock != clock) throw new IllegalArgumentException();
		return add(time.milliseconds, time.nanoseconds, dest);
	}
	
	public void addInterarrivalTo(AbsoluteTime timeAndDestination)
	{
		if(timeAndDestination == null) throw new IllegalArgumentException();
		timeAndDestination.add(this, timeAndDestination);
	}
	
	public RelativeTime getInterarrivalTime()
	{
		return (RelativeTime)clone();
	}
	
	public RelativeTime getInterarrivalTime(RelativeTime destination)
	{
		if(destination == null) return new RelativeTime(this);
		destination.milliseconds = milliseconds;
		destination.nanoseconds = nanoseconds;
		destination.clock = clock;
		return destination;
	}
	
	public RelativeTime relative(Clock clock)
	{
		return relative(clock, null);
	}
	
	public RelativeTime relative(Clock clock, RelativeTime dest)
	{
		if(clock == null) clock = Clock.getRealtimeClock();
		if(dest == null) dest = new RelativeTime(0,0,clock);
		else dest.clock = clock;
		dest.milliseconds = milliseconds;
		dest.nanoseconds = nanoseconds;
		return dest;
	}
	
	public RelativeTime subtract(RelativeTime time)
	{
		return subtract(time, null);
	}
	
	public RelativeTime subtract(RelativeTime time, RelativeTime dest)
	{
		if (time == null) throw new IllegalArgumentException();
		if( time.clock != clock) throw new IllegalArgumentException();
        if (dest == null) dest = new RelativeTime(0, 0, clock);
        if (!dest._setnormalised(addSafe(this.milliseconds, -time.milliseconds), this.nanoseconds - time.nanoseconds) ) throw new ArithmeticException();
        dest.clock = clock;
        return dest;
	}
	
	public String toString()
	{
		return "(" + milliseconds + " ms, " + nanoseconds + " ns)";
	}
}
